
#include "./../io_epoll.h"
#include "./../../socket_module/socket_module_define.h"
#include "test_util.h"
#include "./../../log_module/log_module_define.h"
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <gtest/gtest.h>
#include <memory>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>

TEST(IoEpoll, Test)
{
    IoEpoll epoll;
    int client_socket = CreateClientSocket();
    if (client_socket)
    {
        printf("new connection client socket (%d).\n", client_socket);
        struct epoll_event event;
        event.events  = EPOLLIN | EPOLLOUT | EPOLLONESHOT | EPOLLRDHUP;
        event.data.fd = client_socket;
        epoll.AddEpollEvent(client_socket, event);
        std::string data = "GET / HTTP/1.1\r\nUser-Agent: curl/8.4.0-DEV\r\n\r\n";
        send(client_socket, data.c_str(), data.size(), 0);
    }
    SocketBuffer socket_buffer(std::make_shared<TcpSocket>(client_socket));
    while (true && client_socket)
    {
        int need_break_loop = 0;
        std::vector<struct epoll_event> event_vec(10);
        int result_size = epoll.EpollWait(event_vec);
        for (int index = 0; index < result_size; index++)
        {
            struct epoll_event & event = event_vec.at(index);
            printf("socket fd : %d.\n", event.data.fd);
            printf("event & EPOLLIN : %d.\n", event.events & EPOLLIN);
            printf("event & EPOLLOUT : %d.\n", event.events & EPOLLOUT);
            printf("event & EPOLLRDHUP : %d.\n", event.events & EPOLLRDHUP);
            printf("event & EPOLLERR : %d.\n\n", event.events & EPOLLERR);
            if (event.events & EPOLLOUT)
            {
                struct epoll_event repeat_event;
                memset(&repeat_event, 0 , sizeof repeat_event);
                repeat_event.data.fd = event.data.fd;
                repeat_event.events  = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT;
                epoll.ModEpollEvent(event.data.fd, repeat_event);
            }
            else if (event.events & EPOLLIN)
            {
                if (event.events & EPOLLRDHUP) 
                {
                    printf("will move client socket(%d).\n", event.data.fd);
                    epoll.DelEpollEvent(event.data.fd);
                    need_break_loop = 1;
                    printf("%s.\n", socket_buffer.GetBufferData());
                    break;
                }
                else
                { 
                    int result = socket_buffer.ReadData();
                    if (result < 0)
                    {
                        printf("read data result size : %d.\n", result);
                        printf("%s.\n", socket_buffer.GetBufferData());
                        epoll.DelEpollEvent(event.data.fd);
                        need_break_loop = 1;
                        break;
                    }
                    struct epoll_event repeat_event;
                    repeat_event.events = EPOLLIN | EPOLLONESHOT | EPOLLRDHUP | EPOLLERR;
                    repeat_event.data.fd = event.data.fd;
                    epoll.ModEpollEvent(event.data.fd, repeat_event);
                }
            }
        }
        if (need_break_loop)
        {
            break;
        }
    }
    close(client_socket);
}

TEST(IoEpoll, ServerSocket)
{
    IoEpoll epoll;
    int server_socket = CreateServerSocket();
    {
        printf("new server socket (%d).\n", server_socket);
        struct epoll_event event;
        event.events  = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLOUT;
        event.data.fd = server_socket;

        epoll.AddEpollEvent(server_socket, event);
    }
    std::map<int, std::shared_ptr<SocketBuffer>> m_client_socket_map;
    while (true)
    {
        std::vector<struct epoll_event> event_vec(10);
        int result_size = epoll.EpollWait(event_vec);
        if (result_size > 0)
        {
            for (int index = 0; index < result_size; index++)            
            {
                struct epoll_event event = event_vec.at(index);

                printf("trigger socket (%d).\n", event.data.fd);
                printf("event & EPOLLIN : %d.\n", event.events & EPOLLIN);
                printf("event & EPOLLOUT : %d.\n", event.events & EPOLLOUT);
                printf("event & EPOLLRDHUP : %d.\n", event.events & EPOLLRDHUP);
                printf("event & EPOLLERR : %d.\n", event.events & EPOLLERR);
                if (event.data.fd == server_socket)
                { 
                    if (event.events & EPOLLOUT)
                    { 
                        struct epoll_event repeat_event;
                        repeat_event.events = EPOLLIN | EPOLLRDHUP | EPOLLERR;
                        repeat_event.data.fd = server_socket;

                        epoll.ModEpollEvent(server_socket, repeat_event);
                    }
                    else if (event.events & EPOLLIN)
                    {
                        int new_client_socket = accept(server_socket, nullptr, nullptr);
                        if (new_client_socket > 0)
                        {
                            printf("accept client socket (%d).\n", new_client_socket);
                            struct epoll_event evnt;
                            evnt.events  = EPOLLOUT | EPOLLIN | EPOLLRDHUP | EPOLLONESHOT | EPOLLERR;
                            evnt.data.fd = new_client_socket;

                            m_client_socket_map[new_client_socket] = std::make_shared<SocketBuffer>(std::make_shared<TcpSocket>(new_client_socket));
                            epoll.AddEpollEvent(new_client_socket, evnt);
                        }
                        struct epoll_event repeat_event;
                        repeat_event.events = EPOLLIN | EPOLLRDHUP | EPOLLERR;
                        repeat_event.data.fd = server_socket;

                        epoll.ModEpollEvent(server_socket, repeat_event);
                    }
                }
                else
                { 
                    if (event.events & EPOLLIN)
                    {
                        auto iter = m_client_socket_map.find(event.data.fd);
                        if (iter == m_client_socket_map.end())
                        {
                            printf("leak socket id : %d.\n", event.data.fd);
                            continue;
                        }
                        auto size = iter->second->ReadData();
                        if (size >= 0)
                        {
                            struct epoll_event evt;
                            evt.events = EPOLLIN | EPOLLRDHUP | EPOLLONESHOT;
                            evt.data.fd = event.data.fd;
                            epoll.ModEpollEvent(event.data.fd, evt);
                        }
                        else
                        {
                            epoll.DelEpollEvent(event.data.fd);
                            m_client_socket_map.erase(iter);
                        }
                    }
                    else if (event.events & EPOLLOUT)
                    { 
                        struct epoll_event evt;
                        evt.events = EPOLLIN | EPOLLRDHUP | EPOLLERR | EPOLLONESHOT;
                        evt.data.fd = event.data.fd;
                        epoll.ModEpollEvent(event.data.fd, evt);
                    }
                }
            }
        }
    }
    close(server_socket);
}

int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);

    LogModuleConfig config;
    config.m_file_name = argv[0];
    LogModule::Instance()->InitModule(config);
    return RUN_ALL_TESTS();
}
